package com.wits100.av;

import android.media.MediaFormat;

import java.nio.ByteBuffer;

/**
 * Created by chuanjiang.zh on 2016-08-07.
 */
public class MFormat {

    public int codec;		/// 视频编码  @see MCodec
    public int width;		/// 视频高
    public int height;		/// 视频宽
    public int fps;		    /// 帧率
    public int profile;
    public int clockRate;

    public int audioCodec;	/// 音频编码  @see MCodec
    public int channels;	/// 通道数
    public int sampleRate;	/// 采样率
    public int audioProfile;	/// 档次
    public int audioRate;       ///

    public byte[] props;    /// 视频解码参数, 对于H.264是sps+pps, 对于H.265是vps+sps+pps

    public byte[] vps;
    public byte[] sps;
    public byte[] pps;


    public byte[] audioConfig;


    public boolean isValid() {
        return (codec != MediaType.MCODEC_NONE) || (audioCodec != MediaType.MCODEC_NONE);
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("codec: " + codec + ",");
        sb.append("width: " + width + ",");
        sb.append("height: " + height + ",");
        sb.append("profile: " + profile + ",");
        sb.append("fps: " + fps + ",");

        if (props != null) {
            sb.append("props: " + props.length + ",");
        }

        if (audioConfig != null) {
            sb.append("audioConfig: " + audioConfig.length + ",");
        }

        return sb.toString();
    }

    public MediaFormat toVideoFormat() {
        MediaFormat fmt = new MediaFormat();

        String mime = getMime(codec);
        fmt.setString(MediaFormat.KEY_MIME, mime);

        fmt.setInteger(MediaFormat.KEY_WIDTH, width);
        fmt.setInteger(MediaFormat.KEY_HEIGHT, height);

        if (codec == MediaType.MCODEC_H264) {
            if (sps != null && pps != null) {
                byte[] csd0 = addStartcode(sps);
                fmt.setByteBuffer("csd-0", ByteBuffer.wrap(csd0));

                byte[] csd1 = addStartcode(pps);
                fmt.setByteBuffer("csd-1", ByteBuffer.wrap(csd1));
            }
        } else if (codec == MediaType.MCODEC_H265) {
            if (sps != null && pps != null && vps != null) {
                byte[] csd0 = addStartcode(vps);
                fmt.setByteBuffer("csd-0", ByteBuffer.wrap(csd0));

                byte[] csd1 = addStartcode(sps);
                fmt.setByteBuffer("csd-1", ByteBuffer.wrap(csd1));

                byte[] csd2 = addStartcode(pps);
                fmt.setByteBuffer("csd-2", ByteBuffer.wrap(csd2));
            }
        } else {
            if (props != null && (props.length > 0)) {
                ByteBuffer csd = ByteBuffer.wrap(props);
                fmt.setByteBuffer("csd-0", csd);
            }
        }

        return fmt;
    }

    public MediaFormat toAudioFormat() {
        MediaFormat fmt = new MediaFormat();

        String mime = getMime(audioCodec);
        fmt.setString(MediaFormat.KEY_MIME, mime);

        fmt.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate);
        fmt.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channels);

        if (audioCodec == MediaType.MCODEC_AAC) {
            // ffmpeg 中定义的profile 总是比 android 中定义的多 1.
            // MediaCodecInfo.CodecProfileLevel.AACObjectLC
            //fmt.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            int aacProfile = audioProfile + 2;
            fmt.setInteger(MediaFormat.KEY_AAC_PROFILE, aacProfile);

            ByteBuffer csd = ByteBuffer.allocate(2);
            int srate = getFreqIndex(sampleRate);
            byte b1 = (byte)((aacProfile) << 3 | srate >> 1);
            byte b2 = (byte)((srate & 0x01) << 7 | channels << 3);
            csd.put(0, b1);
            csd.put(1, b2);
            fmt.setByteBuffer("csd-0", csd);
        }
        else if(audioConfig != null && audioConfig.length > 0) {
            ByteBuffer csd = ByteBuffer.wrap(audioConfig);
            fmt.setByteBuffer("csd-0", csd);
        }

        return fmt;
    }


    static class CodecMime {
        public int codec;
        public String mime;

        CodecMime(int codec, String mime)
        {
            this.codec = codec;
            this.mime = mime;
        }
    }


    static public String getMime(int codec) {
        CodecMime[] mimes = new CodecMime[]{
                new CodecMime(MediaType.MCODEC_H264, MediaFormat.MIMETYPE_VIDEO_AVC),
                new CodecMime(MediaType.MCODEC_H265, MediaFormat.MIMETYPE_VIDEO_HEVC),
                new CodecMime(MediaType.MCODEC_MJPEG, "video/x-motion-jpeg"),
                new CodecMime(MediaType.MCODEC_AAC, MediaFormat.MIMETYPE_AUDIO_AAC),
                new CodecMime(MediaType.MCODEC_G711A, MediaFormat.MIMETYPE_AUDIO_G711_ALAW),
                new CodecMime(MediaType.MCODEC_G711U, MediaFormat.MIMETYPE_AUDIO_G711_MLAW),
        };

        for (CodecMime mime: mimes) {
            if (mime.codec == codec) {
                return mime.mime;
            }
        }
        return "";
    }

    public static byte[] addStartcode(byte[] nalu) {
        byte[] startcode = new byte[] {0, 0, 0, 1};
        byte[] bytes = new byte[nalu.length + startcode.length];
        System.arraycopy(startcode, 0, bytes, 0, startcode.length);
        System.arraycopy(nalu, 0, bytes, startcode.length, nalu.length);
        return bytes;
    }

    public static int freqTable[] = {
            96000, 88200, 64000, 48000,
            44100, 32000, 24000, 22050,
            16000, 12000, 11025, 8000,
            7350, 0, 0, 0};

    public static int getFreqIndex(int freq) {
        for (int i = 0 ;i < freqTable.length; i ++) {
            if (freq == freqTable[i]) {
                return i;
            }
        }
        return freqTable.length - 1;
    }

    public static byte[] makeAacConfig(int profile, int channels, int freq) {
        byte[] config = new byte[2];

        int srate = getFreqIndex(freq);
        config[0] = (byte)(profile << 3 | srate>> 1);
        config[1] = (byte)((srate & 0x01) << 7 | channels << 3);
        return config;
    }
}
